import { getType } from "./base";

/**
 * 存储数据
 * @param {String, Object} key 字段名
 * @param value 字段值
 * @param expiryTime 失效时间:毫秒
 * @return void
 */
export const setStore = (key = "", value = "", expiryTime) => {
  if (!key) return;
  if (typeof key === "string") {
    key = {
      [key]: value,
    };
  }

  Object.keys(key).forEach((dataKey) => {
    let curTimer = new Date().getTime();
    let dataValue = JSON.stringify({
      time: curTimer,
      content: key[dataKey],
      expiryTime: expiryTime ? curTimer + Number(expiryTime) : null,
    });
    localStorage.setItem(dataKey, dataValue);
  });
};

/**
 * 获取数据
 * @param {String, Array} key 键名
 * @param Boolean isAll 是否为全部
 * @return {String, Object}
 */
export const getStore = (key = "", isAll = false) => {
  if (key === "" && !isAll) return null;
  let dataKeys = isAll ? Object.getOwnPropertyNames(localStorage) : Array.isArray(key) ? key : [key];
  let dataRes = {};
  dataKeys.forEach((dataKey) => {
    let itemData = localStorage.getItem(dataKey) || null;
    dataRes[dataKey] = null;
    if (isJson(itemData)) {
      let jsonData = JSON.parse(itemData);
      //是否设置过期时间
      if (jsonData.expiryTime) {
        //是否在有效期内
        if (jsonData.expiryTime > new Date().getTime()) {
          dataRes[dataKey] = jsonData.content;
        } else {
          console.warn(`Store -> ${dataKey} 已失效！`);
          deleteStore(dataKey);
        }
      } else {
        dataRes[dataKey] = jsonData.content;
      }
    }
  });
  return typeof key === "string" ? dataRes[key] : dataRes;
};

/**
 * 删除数据
 * @param {String, Array} key 键名
 * @return void
 */
export const deleteStore = (key = "") => {
  let removeKeys = Array.isArray(key) ? key : [key];
  removeKeys.forEach((dataKey) => {
    localStorage.removeItem(dataKey);
  });
};

/**
 * 清空Store
 * @return void
 */
export const clearStore = () => {
  localStorage.clear();
};

/**
 * 通过key找到在列表中对应的显示
 * @param {Object} obj
 *  @param obj.dataList 数据列表
 *  @param obj.value    数据的值对应的字段名称   例如 'value'
 *  @param obj.label    数据的说明对应的字段名称 例如 'label'
 *  @param obj.data     当前传入的数据值
 * @return {String} name 返回当前传入值在数组中对应的名字
 */
export const getDataName = (obj) => {
  let name = obj.data;
  if (Array.isArray(obj.dataList) && obj.dataList.length > 0) {
    for (let i = 0; i < obj.dataList.length; i++) {
      if (obj.dataList[i][obj.value] === obj.data) {
        name = obj.dataList[i][obj.label];
      }
    }
  }
  return name;
};

/**
 * json对象转FormData
 * @param obj
 * @returns {*}
 */
export const jsonToFormData = (obj, oldFormData) => {
  let formData = oldFormData || new FormData();
  if (obj) {
    for (let k in obj) {
      formData.append(k, obj[k]);
    }
  }
  return formData;
};

/**
 * 移除对象中的空字符串
 * @param test
 * @param recurse
 */
export const deleteEmptyString = (test, recurse) => {
  for (let i in test) {
    if (test[i] === "") {
      delete test[i];
    } else if (recurse && typeof test[i] === "object") {
      deleteEmptyString(test[i], recurse);
    }
  }
};

/**
 * 删除对象中的空Key
 * @param test
 * @param recurse
 */
export const deleteEmptyObject = (test, recurse) => {
  for (let i in test) {
    if (test[i] === undefined || test[i] === null || test[i] === "") {
      delete test[i];
    } else if (recurse && typeof test[i] === "object") {
      deleteEmptyObject(test[i], recurse);
    }
  }
};

/**
 * 深度拷贝
 * @param {*} obj
 */
export const deepCloneObject = (obj) => {
  let objClone = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === "object") {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        //判断ojb子元素是否为对象，如果是，递归复制
        if (obj[key] && typeof obj[key] === "object") {
          objClone[key] = deepCloneObject(obj[key]);
        } else {
          //如果不是，简单复制
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
};

/**
 * 获取页面参数
 * @param key 参数名称,不传则返回全部参数
 */
export const getQueryParam = (key) => {
  let paramValue = "";
  let hash = window.location.hash;
  let paramStr = hash ? (hash.indexOf("?") ? hash.split("?")[1] : "") : "";
  let paramObj = {}; //参数对象
  if (paramStr) {
    paramStr.split("&").forEach((param) => {
      let arr = param.split("=");
      let paramKey = arr[0];
      let value = decodeURIComponent(arr[1]);
      paramObj[paramKey] = value;
      if (key && key === paramKey) {
        paramValue = value;
      }
    });
  }

  if (key) return paramValue;
  return paramObj;
};

/**
 * json参数转为query
 * @param Object paramObj
 * @return String user=1&sex=0
 */
export const paramToQS = (paramObj) => {
  if (!(paramObj instanceof Object)) return "";
  return Object.keys(paramObj)
    .map((parkey) => `${parkey}=${encodeURIComponent(paramObj[parkey])}`)
    .join("&");
};

/**
 * 线性数据转化为树
 * @param {Object} data 源数据
 * @param {Object} parentKey 父级id key
 * @param {childrenKey} childrenKey 子集key
 * @param {Object} pId 父级标识符
 */
export const toTree = (data, parentKey, childrenKey, pId) => {
  let tree = [];
  let temp = null;
  for (let i = 0; i < data.length; i++) {
    if (data[i][parentKey] === pId) {
      let obj = data[i];
      temp = toTree(data, parentKey, childrenKey, data[i][childrenKey]);
      if (temp.length > 0) {
        obj.children = temp;
      }
      tree.push(obj);
    }
  }
  return tree;
};

/**
 * 树形转为list
 * @param {*} data
 * @return {array}
 */
export const treeToList = (data) => {
  const res = [];
  let stack = data;
  while (stack.length > 0) {
    const item = stack.shift();
    if (item.children && item.children.length > 0) {
      stack = stack.concat(item.children);
    }
    res.push(item);
  }
  stack = null;
  return res;
};

/**
 * 转换Key
 * @param obj
 * @param keyMap
 * @param isDeep Boolean 是否深度
 * @return Object/Array
 */
export const convertKey = (obj, keyMap, isDeep) => {
  if (!["array", "object"].includes(getType(obj))) {
    throw new TypeError("The first argument should be either an object or an array！");
  }
  if ({}.toString.call(keyMap) !== "object") {
    throw new TypeError("The parameter keyMap should be an object!");
  }
  let res = obj instanceof Array ? [] : {};
  if (obj instanceof Object) {
    for (let key in obj) {
      let newKey = Object.keys(keyMap).includes(key) ? keyMap[key] : key;
      res[newKey] = obj[key];

      //是否为深度转换
      if (isDeep && obj[key] instanceof Object && Object.keys(obj[key]).length) {
        res[newKey] = convertKey(obj[key], keyMap, isDeep);
      }
    }
  }
  return res;
};

/**
 * 移除list/map中空值
 * @param {Object} data
 * @param {Boolean} isDeep 是否深度
 * @param {Array} emptyValueList 空值列表
 * @return {Object|Array}
 */
export const removeEmptyValue = (data, isDeep, emptyValueList) => {
  if (!["array", "object"].includes(getType(data))) {
    throw new TypeError("The data should be an array or an object！");
  }
  let res = Array.isArray(data) ? [] : {};
  const emptyValue = emptyValueList || [null, undefined, ""];
  for (let key in data) {
    let itm = data[key];
    if (!emptyValue.includes(itm)) {
      Array.isArray(res) ? res.push(itm) : (res[key] = itm);
    }
    let isWalk = ["array", "object"].includes(getType(itm));
    if (isDeep && isWalk) {
      let subData = removeEmptyValue(itm, isDeep, emptyValueList);
      res[key] = subData;
    }
  }
  return res;
};

/**
 * 设置tree层级信息
 * @param {Object} data
 * @param {Object} maxLevel 最多层级
 * @param {Object} currLevel 当前层级，可不传
 */
export const setTreeHierarchyIndex = function (data, maxLevel = 10, currLevel) {
  if (!(data instanceof Array)) throw new TypeError("The data should be an array!");
  let result = [];
  for (let k = 0; k < data.length; k++) {
    // console.log('当前层级：', currLevel);
    // console.log('是否在指定层级范围：', currLevel < maxLevel);
    let temp = data[k];
    let newNode = {
      ...temp,
      hierarchyIndex: currLevel + 1,
      children: null,
    };
    delete newNode.children;
    //是否在指定层级范围
    if (currLevel >= maxLevel) continue;
    if (temp.children && temp.children.length > 0) {
      currLevel++;
      newNode.children = setTreeHierarchyIndex(temp.children, maxLevel, currLevel);
      currLevel--;
    }
    result.push(newNode);
  }
  return result;
};

/**
 * 复制数据
 * @param {*}  data
 * @param {Number}  count
 * @return {*}
 */
export const copyData = (data, count = 5) => new Array(count).fill(data);

/**
 * 格式化金钱，每千分位加逗号
 * @param {String} str
 */
export const formatMoney = (str = "") => {
  if (!str) return "";
  str = str.toString();
  let s = "";
  let count = 0;
  for (let i = str.length - 1; i >= 0; i--) {
    count++;
    s = str[i] + s;
    if (count % 3 == 0 && i != 0) s = "," + s;
  }
  return s;
};

/**
 * 扁平化数组
 * @param {Array} arr
 * @returns {Array}
 */
export const flatArr = (arr = []) => {
  return arr.reduce((item, next) => item.concat(Array.isArray(next) ? flatArr(next) : next, []), []);
};

/**
 * 是否为json
 * @param {*} data
 * @returns {Boolean}
 */
export const isJson = (data = null) => {
  try {
    if (typeof data !== "string") return false;
    let jsonObj = JSON.parse(data);
    return !!(jsonObj && {}.toString.call(jsonObj) === "object");
  } catch (error) {
    console.log(error);
    return false;
  }
};

/**
 * 删除array/obj中的Key
 * @param {Array|Object} data
 * @param {Array} keys
 * @returns {Array|Object}
 */
export const deleteMapKey = (data = null, keys = null) => {
  keys = keys instanceof Array ? keys : keys ? [keys] : keys;
  const dataType = {}.toString.call(data).slice(8, -1).toLowerCase();
  if (!["array", "object"].includes(dataType) || !(keys instanceof Array)) return data;

  //生成新的Map
  let genNewMap = (obj, diskeys) => {
    Object.keys(obj).forEach((key) => {
      keys.includes(key) && delete obj[key];
    });
    return obj;
  };

  //array or obj
  if (dataType === "array") {
    return data.map((item) => (typeof item === "object" ? genNewMap(item, keys) : item));
  } else {
    return genNewMap(data, keys);
  }
};

/**
 * 列表转换为tree(浅拷贝版本)
 * @param {Array} list 数据列表
 * @param {String|Number} rootId 根节点
 * @param {Object} options 配置项
 *  @param {String} options.idKey 唯一值
 *  @param {String} options.parentIdKey 父级唯一值
 *  @param {String} options.childKey 子数据
 * @return {Array}
 */
export const listToTree = (list, rootId = null, options) => {
  if (!Array.isArray(list)) {
    new Error("The first argument should be an array");
    return [];
  }
  const { idKey, parentIdKey, childKey } = options || {
    idKey: "id",
    parentIdKey: "parentId",
    childKey: "children",
  };
  const dataMap = {};
  const resList = [];
  for (let i = 0, len = list.length; i < len; i++) {
    const item = list[i];
    const id = item[idKey];
    const parentId = item[parentIdKey];

    // 该元素可能已经存在map中
    dataMap[id] = dataMap[id] ? Object.assign({}, item, dataMap[id]) : item;

    // 映射关系那一项
    const treeItem = dataMap[id];

    // 是否为根节点
    if (item[parentIdKey] === rootId) {
      resList.push(treeItem);
    } else {
      // 若父元素不存在，初始化父元素
      if (!dataMap[parentId]) {
        dataMap[parentId] = {};
      }
      // 若无根元素则放入在map中
      if (!dataMap[parentId][childKey]) {
        dataMap[parentId][childKey] = [];
      }
      dataMap[parentId][childKey].push(treeItem);
    }
  }
  return resList;
};

/**
 * 获取数据
 * @param {Object|Array} source 资源
 * @param {Array|String} path 路径  例：user.job.name 或 [‘user’, 'job', 'name']
 * @param {*} defaultValue 默认值
 * @param {Boolean} isDeep 是否为深度
 * @return {*}
 */
export const get = (source, path, defaultValue, isDeep) => {
  try {
    if (!source) return defaultValue;
    if (!path || (Array.isArray(path) && path.length === 0)) return defaultValue || source;
    var paths = Array.isArray(path) ? path : path.replace(/\[(\d+)\]/g, ".$1").split(".");
    var res = isDeep ? JSON.parse(JSON.stringify(source)) : source;
    for (var i = 0, len = paths.length; i < len; i++) {
      var key = paths[i].toString();
      if (typeof res === "string" || !{}.hasOwnProperty.call(res, key)) {
        return defaultValue;
      }

      res = res[key]; //记录当前的
    }
    return res;
  } catch (e) {
    return defaultValue;
  }
};

/**
 * 获取对象key
 * @param {Object} obj 对象
 * @param {Function} cb 回调函数
 * @param {*} defaultValue 默认值
 * @return {String} key
 */
export const findObjectKey = (obj, cb, defaultValue) => {
  for (let key in obj) {
    const res = cb ? cb(obj[key], key) : false;
    if (res) {
      return key;
    }
  }
  return defaultValue;
};

/**
 * 删除list某一项
 * @param {Array} list 源数据
 * @param {Number|Function} delIdx 下标||每次执行回调函数
 * @param {Boolean} isModifyTarget 是否改变原数组, 默认返回新数组
 * @return {Array}
 */
export const listDeleteBy = (list, delIdx = -1, isModifyTarget = false) => {
  if (!Array.isArray(list) || delIdx === -1) return list;
  if (typeof delIdx === "number") {
    // 是否改变原数组
    if (isModifyTarget) {
      list.splice(delIdx, 1);
      return list;
    }
    return list.reduce((pre, item, idx) => {
      if (idx !== delIdx) {
        pre.push(item);
      }
      return pre;
    }, []);
  } else if (typeof delIdx === "function") {
    let deleteIndex = -1;
    const newList = list.reduce((pre, cur, idx) => {
      const cbRes = delIdx(cur, idx);
      if (cbRes) {
        deleteIndex = idx;
        return pre;
      }
      pre.push(cur);
      return pre;
    }, []);

    // 是否改变原数组
    if (!isModifyTarget) return newList;
    deleteIndex > -1 && list.splice(deleteIndex, 1);
    return list;
  }
  return list;
};

/**
 * 列表分组
 * @param {Array} list 列表
 * @param {Function} cb 回调函数，分组所使用字段
 * @return {Array}
 */
export const listGroupBy = (list, cb) => {
  if (!Array.isArray(list)) return {};
  return list.reduce((wrap, cur, idx) => {
    const groupKey = cb ? cb(cur, idx) : null;
    if (!groupKey) return wrap;
    if (!wrap[groupKey]) {
      wrap[groupKey] = [];
    }
    wrap[groupKey].push(cur);
    return wrap;
  }, {});
};

/**
 * 数组更新
 * @param {Array} list
 * @param {*} newItem 新的item
 * @param {Function} cb 每次回调
 * @param {Boolean} isCombineOrigin 是否需要合并原始
 * @return {Array}
 */
export const listUpdateBy = (list, newItem, cb, isCombineOrigin = false) => {
  return list.reduce((res, cur, idx) => {
    const cbRes = cb ? cb(itm, idx) : false; // 执行每次回调
    const resItem = cbRes ? (isCombineOrigin ? { ...cur, ...newItem } : newItem) : cur;
    res.push(resItem);
    return res;
  }, []);
};

/**
 * 数据更新
 * @param {Array} list
 * @param {String} key
 * @param {*} newItem
 * @returns
 */
export const listUpdate = (list, key, newItem) => {
  return list.reduce((res, cur) => {
    res.push(cur[key] === newItem[key] ? newItem : cur);
    return res;
  }, []);
};

/**
 * 函数合并
 * @returns {*}
 */
export const funCombine = (...[firstFun, ...otherFuns]) => {
  return (...args) => {
    let res = firstFun(...args);
    otherFuns.forEach((fn) => {
      res = typeof fn === "function" ? fn(res) : res;
    });
    return res;
  };
};

/**
 * 字符串模板解析
 * @param {String} tplString 模板字符，如：{{name}}长得很帅啊！
 * @param {Object} data 数据源
 * @return {String}
 */
export const stringTplParse = (tplString, data) => {
  Object.keys(data).forEach((key) => {
    tplString = tplString.replace(new RegExp(`{{${key}}}`, "g"), data[key]);
  });
  return tplString;
};

/**
 * 数组扁平化
 * @param {Array} arr
 * @return {Array}
 */
export const arrayFlatten = (arr) => {
  let res = [];
  const stack = [...arr];
  while (stack.length) {
    const next = stack.pop();
    if (Array.isArray(next)) {
      stack.push(...next);
    } else {
      res.push(next);
    }
  }
  return res.reverse();
};
/*
 * 驼峰装字符串
 * @param {String} transString
 * @return {String}
 */
export const camelToUnderline = (camelStr) => {
  return camelStr
    .replace(/[A-Z]/g, (s) => {
      return " " + s.toLowerCase();
    })
    .trim()
    .replaceAll(" ", "_");
};

/**
 * 驼峰装字符串
 * @param {String} transString
 * @return {String}
 */
export const camelToMiddleline = (camelStr) => {
  return camelStr
    .replace(/[A-Z]/g, (s) => {
      return " " + s.toLowerCase();
    })
    .trim()
    .replaceAll(" ", "-");
};

/**
 * 下划线转驼峰
 * @param {String} str
 * @return {String}
 */
export const lineToHump = (str) => {
  return str.toLowerCase().replace(/([a-z])|-([a-z])/g, (s, s1) => {
    return s1.toUpperCase();
  });
};

/**
 * 对象转为Map
 * @param {Object} obj
 * @return {Map}
 */
export const objectToMap = (obj) => {
  const mapRes = new Map();
  for (const key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      mapRes.set(key, obj[key]);
    }
  }
  return mapRes;
};

/**
 * map转为对象
 * @param {Map} map
 * @return {Object}
 */
export const mapToObject = (map) => {
  const obj = {};
  if (getType(map) !== "map") return obj;
  map.forEach((val, key) => {
    obj[key] = val;
  });
  return obj;
};

/**
 * 对象扁平化
 * @param {Object} source
 * @param {String|Function} joinChar
 * @returns {Object}
 */
export const objectFlat = (source, joinChar = ".") => {
  const Utils = {
    // 对象
    isObject: (d) => {
      return {}.toString.call(d) === "[object Object]";
    },
    // 获取key的路径
    getKeyPath: (key, path, value) => {
      if (typeof joinChar === "function") {
        return joinChar(key, path, value);
      }
      return path.length > 0 ? path.join(joinChar) + joinChar + key : key;
    },
  };

  // 执行方法
  function _walk(data, keyPath = []) {
    let result = {};
    for (let key in data) {
      const value = data[key];
      // 如果为对象，则需要继续递归往下找，同时需要传递当前路径给下级使用；
      if (Utils.isObject(value)) {
        const childrenData = _walk(value, [...keyPath, key]);
        result = Object.assign({}, result, childrenData);
      } else {
        const _keyPath = Utils.getKeyPath(key, keyPath, value);
        result[_keyPath] = value;
      }
    }
    return result;
  }
  const res = Utils.isObject(source) ? _walk(source, []) : {};
  return res;
};
